package com.example.mydownloader.mymodule.http

import android.util.Base64
import com.example.mydownloader.myconst.TransmissionData
import com.example.mydownloader.mymodule.encrypt.AESCryptUtil
import com.example.mydownloader.mymodule.encrypt.AESKey
import com.example.mydownloader.mymodule.encrypt.MessageDigestUtil
import com.example.mydownloader.mymodule.encrypt.RSAUtil
import com.example.mydownloader.mymodule.log.LogUtil
import okhttp3.FormBody
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.Request
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import okio.Buffer
import java.io.File


/**
 * <Description>
 * 封装HTTP请求以及加解密请求
 * @author Wallace
 * @Date 2021/11/24 16:14
 */

/**
 * 对请求头的处理
 */
fun Request.Builder.addHeaders(headers: HashMap<String, String>?): Request.Builder {
    headers?.let {
        headers.forEach { header ->
            this.addHeader(header.key, header.value)
        }
    }
    return this
}

fun Request.Builder.removeHeaders(headers: HashSet<String>?): Request.Builder {
    headers?.let {
        headers.forEach {
            this.removeHeader(it)
        }
    }
    return this
}

/**
 * 对body的处理
 */
fun Request.Builder.addFormBody(bodyParams: HashMap<String, String>?): Request.Builder {
    bodyParams?.let {
        val formBodyBuilder = FormBody.Builder()
        bodyParams.forEach { bodyParam ->
            formBodyBuilder.add(bodyParam.key, bodyParam.value)
        }
        val formBody = formBodyBuilder.build()
        this.post(formBody)
    }
    return this
}

fun Request.Builder.addMultipartBody(bodyParams: HashMap<String, Any>?): Request.Builder {
    bodyParams?.let {
        val multipartBodyBuilder = MultipartBody.Builder()
        bodyParams.forEach { bodyParam ->
            if (bodyParam.value is String) {
                multipartBodyBuilder.addFormDataPart(bodyParam.key, bodyParam.value as String)
            } else if (bodyParam.value is File) {
                val file = bodyParam.value as File
                val fileBody =
                    file.asRequestBody("multipart/form-data; charset=utf-8".toMediaType())
                val requestBody = MultipartBody.Builder()
                    .addFormDataPart(file.name, file.name, fileBody)
                    .build()
                multipartBodyBuilder.addFormDataPart(
                    bodyParam.key, (bodyParam.value as File).name,
                    requestBody
                )
            }
        }
        val multipart = multipartBodyBuilder.build()
        this.post(multipart)
    }
    return this
}

fun encryptRequest(
    request: Request,
    aesKey: AESKey,
    publicKeyRSA: ByteArray
): Request {
    var newRequest: Request? = null
    val requestBody = request.body
    requestBody?.apply {
        if (this.contentLength() > 0) {
            val requestBuffer = Buffer()
            this.writeTo(requestBuffer)
            val oldBodyString = requestBuffer.readUtf8()
            LogUtil.d("bodyString = $oldBodyString")
            requestBuffer.clear()
            // 使用MD5对字符串做摘要处理
            val oldBodyStringMD5 = MessageDigestUtil.md5(oldBodyString)
            // 使用RSA公钥对MD5数据加密生成签名signature
            val signatureByteArray = RSAUtil.encryptByPublicKey1(
                oldBodyStringMD5.toByteArray(),
                publicKeyRSA
            )
            // 用Android的Base64进行编码
            val signature =
                Base64.encode(signatureByteArray, Base64.NO_WRAP).toString(Charsets.UTF_8)
            // 偏移向量加密
            val ivSecretByteArray = RSAUtil.encryptByPublicKey1(aesKey.iv, publicKeyRSA)
            // Base64编码
            val ivSecretString =
                Base64.encode(ivSecretByteArray, Base64.NO_WRAP).toString(Charsets.UTF_8)
            // 使用AES密钥对字符串加密
            val oldBodyStringCipher =
                AESCryptUtil.encryptAndEncodeToString(
                    oldBodyString, aesKey.getSecretKeySpec(), aesKey.iv
                )
            // 使用RSA公钥对AES密钥加密生成keyByteArray
            val keyByteArray =
                RSAUtil.encryptByPublicKey1(aesKey.key, publicKeyRSA)
            // 使用Android的Base64工具将keyByteArray转码成keyStringBase64
            val keyStringBase64 =
                Base64.encode(keyByteArray, Base64.NO_WRAP).toString(Charsets.UTF_8)
            // 打印加密后的数据
            LogUtil.d("signature = $signature")
            LogUtil.d("MD5 = $oldBodyStringMD5")
            LogUtil.d("SecurityKey = $keyStringBase64")
            LogUtil.d("iv = $ivSecretString")
            // 构建新的Request请求
            val newBuilder = request.newBuilder()
            newRequest = newBuilder.addHeaders(
                hashMapOf(
                    // 将签名signature放到请求头Authentication中
                    TransmissionData.RSA_SIGNATURE to (signature),
                    // 将AES密钥密文放入请求头的SecurityKey中
                    TransmissionData.AES_KEY to (keyStringBase64),
                    // 将AES加密的偏移量放入请求头的iv中
                    TransmissionData.AES_IV to (ivSecretString)
                )
            ).post(oldBodyStringCipher!!.toRequestBody("text/plain".toMediaType())).build()
        }
    }
    return newRequest ?: request
}

fun decryptResponse(
    response: Response,
    aesKey: AESKey
): Response {
    var newResponse: Response? = null
    response.body?.apply {
        val oldResponseBody = this.string()
        LogUtil.d("oldResponseBody = $oldResponseBody")
        val oldResponseBodyByteArray = oldResponseBody.toByteArray()
        val newResponseBodyByteArray =
            AESCryptUtil.decryptAfterDecode(
                oldResponseBodyByteArray,
                aesKey.getSecretKeySpec(),
                aesKey.iv
            )
        this.close()
        LogUtil.d("newResponseBody = ${newResponseBodyByteArray!!.toString(Charsets.UTF_8)}")
        newResponse = response.newBuilder()
            .body(newResponseBodyByteArray.toResponseBody("application/json; charset=utf-8".toMediaType()))
            .build()
    }
    return newResponse ?: response
}